package com.sdweizan.sdk.bytedance.api.impl;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.sdweizan.sdk.bytedance.api.BdApiService;
import com.sdweizan.sdk.bytedance.api.BdMiniAppService;
import com.sdweizan.sdk.bytedance.api.BdOpenService;
import com.sdweizan.sdk.bytedance.bean.notify.BdRequestHeader;
import com.sdweizan.sdk.bytedance.config.BdConfigStorage;
import com.sdweizan.sdk.bytedance.error.BdRuntimeException;
import com.sdweizan.sdk.bytedance.util.*;
import com.sdweizan.sdk.bytedance.util.crypto.SHA1;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class BdMiniAppServiceImpl implements BdMiniAppService {

  @Getter private final BdApiService bdApiService = new BdServiceApiImpl(this);
  @Getter private final BdOpenService bdOpenService = new BdServiceOpenImpl(this);

  private Map<String, BdConfigStorage> configStorageMap;

  @Override
  public boolean checkSignature(String timestamp, String nonce, String msg, String signature) {
    try {
      return SHA1.gen(getConfigStorage().getToken(), timestamp, nonce, msg).equals(signature);
    } catch (Exception e) {
      log.error("Checking signature failed, and the reason is :" + e.getMessage());
      return false;
    }
  }

  @Override
  public boolean checkSignature(BdRequestHeader requestHeader) {
    try {
      String message =
          requestHeader.getTimestamp()
              + "\n"
              + requestHeader.getNonce()
              + "\n"
              + requestHeader.getBody()
              + "\n";
      Signature sign = Signature.getInstance("SHA256withRSA");
      byte[] decoded = Base64.getDecoder().decode(getConfigStorage().getPlatformPublicKey());
      PublicKey rsaPublicKey =
          KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
      sign.initVerify(rsaPublicKey);
      sign.update(message.getBytes(StandardCharsets.UTF_8));
      return sign.verify(
          Base64.getDecoder()
              .decode(requestHeader.getSignature().getBytes(StandardCharsets.UTF_8)));
    } catch (Exception e) {
      log.error("[通用交易]签名校验失败 :{}", e.getMessage());
      return false;
    }
  }

  @Override
  public BdConfigStorage getConfigStorage() {
    if (configStorageMap.size() == 1) {
      // 只有一个字节小程序，直接返回其配置即可
      return configStorageMap.values().iterator().next();
    }
    return configStorageMap.get(BdConfigStorageHolder.get());
  }

  @Override
  public void setWxMpConfigStorage(BdConfigStorage configStorage) {
    final String defaultMpId = configStorage.getAppId();
    setMultiConfigStorages(ImmutableMap.of(defaultMpId, configStorage), defaultMpId);
  }

  @Override
  public void setMultiConfigStorages(Map<String, BdConfigStorage> configStorages) {
    setMultiConfigStorages(configStorages, configStorages.keySet().iterator().next());
  }

  @Override
  public void setMultiConfigStorages(
      Map<String, BdConfigStorage> configStorages, String defaultMpId) {
    configStorageMap = Maps.newHashMap(configStorages);
    BdConfigStorageHolder.set(defaultMpId);
  }

  @Override
  public void addConfigStorage(String mpId, BdConfigStorage configStorages) {
    synchronized (this) {
      if (configStorageMap == null) {
        setWxMpConfigStorage(configStorages);
      } else {
        BdConfigStorageHolder.set(mpId);
        configStorageMap.put(mpId, configStorages);
      }
    }
  }

  @Override
  public void removeConfigStorage(String mpId) {
    synchronized (this) {
      if (configStorageMap.size() == 1) {
        configStorageMap.remove(mpId);
        log.info("已删除最后一个字节小程序配置：{}，须立即使用setWxMpConfigStorage或setMultiConfigStorages添加配置", mpId);
        return;
      }
      if (BdConfigStorageHolder.get().equals(mpId)) {
        configStorageMap.remove(mpId);
        final String defaultMpId = configStorageMap.keySet().iterator().next();
        BdConfigStorageHolder.set(defaultMpId);
        log.info("已删除默认字节小程序配置，字节小程序【{}】被设为默认配置", defaultMpId);
        return;
      }
      configStorageMap.remove(mpId);
    }
  }

  @Override
  public BdMiniAppService switchoverTo(String mpId) {
    if (configStorageMap.containsKey(mpId)) {
      BdConfigStorageHolder.set(mpId);
      return this;
    }
    throw new BdRuntimeException(String.format("无法找到对应【%s】的字节小程序配置信息，请核实！", mpId));
  }

  @Override
  public boolean switchover(String mpId) {
    if (configStorageMap.containsKey(mpId)) {
      BdConfigStorageHolder.set(mpId);
      return true;
    }
    log.error("无法找到对应【{}】的字节小程序配置信息，请核实！", mpId);
    return false;
  }
}
